package com.leaf.spring.y201906.day17.lock;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.AbstractQueuedSynchronizer;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;

/**
 * 自定义锁 AQS <br/>
 * 
 * @Title:
 * @Description:
 * @Author: leaf
 * @Since:2019年6月17日
 * @Version:1.1.0
 */
public class LeafLock implements Lock {

	private Helper helper = new Helper();

	public class Helper extends AbstractQueuedSynchronizer {

		private static final long serialVersionUID = 1L;

		// 获得锁
		protected boolean tryAcquire(int arg) {

			// 锁被重载的次数
			int state = getState();

			// 0表示没有线程拥有该锁
			if (state == 0) {
				// 利用CAS原理修改state
				if (compareAndSetState(0, arg)) {
					// 当前线程拥有该锁
					setExclusiveOwnerThread(Thread.currentThread());
					return true;
				}
			} else if (getExclusiveOwnerThread() == Thread.currentThread()) {
				// 线程重入
				setState(getState() + arg);
				return true;
			}

			return false;
		}

		// 释放锁
		@Override
		protected boolean tryRelease(int arg) {

			boolean flag = false;

			// 锁被重载的次数
			int state = getState() - arg;

			// 判断释放后是否为0
			if (state == 0) {
				// 释放锁
				setExclusiveOwnerThread(null);
				flag = true;
			}

			setState(state);

			return flag;
		}

		public Condition newConditionObject() {
			return new ConditionObject();
		}

	}

	/**
	 * 获取锁。
	 * 
	 * 如果该锁没有被另一个线程保持，则获取该锁并立即返回，将锁的保持计数设置为 1。
	 * 
	 * 如果当前线程已经保持该锁，则将保持计数加 1，并且该方法立即返回。
	 * 
	 * 如果该锁被另一个线程保持，则出于线程调度的目的，禁用当前线程，并且在获得锁之前，该线程将一 直处于休眠状态，此时锁保持计数被设置为 1。
	 */
	@Override
	public void lock() {
		// 试图获取锁
		helper.acquire(1);
	}

	@Override
	public void lockInterruptibly() throws InterruptedException {
		helper.acquireInterruptibly(1);
	}

	@Override
	public boolean tryLock() {
		return helper.tryAcquire(1);
	}

	@Override
	public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
		return helper.tryAcquireNanos(1, unit.toNanos(time));
	}

	@Override
	public void unlock() {
		helper.release(1);
	}

	@Override
	public Condition newCondition() {
		return helper.newConditionObject();
	}

}
